package shapes3d;

import processing.core.PApplet;
import processing.core.PImage;
import processing.core.PVector;

public class Ellipsoid extends Shape3D{

	private float radX, radY, radZ;
	
	private int phiSteps;
	private int thetaSteps;

	
	/**
	 * 
	 * @param app
	 * @param nbrVslice
	 * @param nbrHslice
	 * @param norms
	 */
	public Ellipsoid(PApplet app, int nbrVslice, int nbrHslice, boolean norms){
		this.app = app;
		phiSteps = nbrVslice + 1;
		thetaSteps = nbrHslice + 1;
		hasNormals = norms;
		hasColor = false;
		
		radX = 50;
		radY = 50;
		radZ = 50;
		coord = new PVector[thetaSteps][phiSteps];
		calcXYZ();

		if(hasNormals){
			norm = new PVector[thetaSteps][phiSteps];
			calcNormals();
		}
	}
	
	public void setRadius(float radiusX, float radiusY, float radiusZ){
		this.radX = radiusX;
		this.radY = radiusY;
		this.radZ = radiusZ;
		calcXYZ();
		if(hasNormals)
			calcNormals();
	}
	
	public void setRadius(float radius){
		this.radX = this.radY = this.radZ = radius;
		calcXYZ();
		if(hasNormals)
			calcNormals();
	}
		
	public void setTexture(String fname){
		skin = app.loadImage(fname);
		if(uvLoc == null){
			uvLoc = new UV[thetaSteps][phiSteps];
			calcUV();
		}
	}
	
	public void setTexture(PImage skin){
		this.skin = skin;
		if(uvLoc == null){
			uvLoc = new UV[thetaSteps][phiSteps];
			calcUV();
		}
	}
	
	protected void calcUV(){
		for(int p = 0; p < phiSteps; p++) {
			for(int t = 0; t < thetaSteps; t++) {
				uvLoc[t][p] = new UV(1.0f -(float)t / (thetaSteps - 1), (float)p / (phiSteps - 1));
			}
		}
	}
	
	protected void calcNormals(){
		PVector c , np, nt;
		
		for(int p = 0; p < phiSteps; p++) {
			for(int t = 0; t < thetaSteps; t++) {
				c = coord[t][p];
				np = coord[t][(p+1)%phiSteps];
				nt = coord[(t+1)%thetaSteps][p];
				norm[t][p] = PVector.cross(PVector.sub(np, c, null), 
									PVector.sub(nt, c, null), 
									null);
			}
		}	
	}
	
	protected void calcXYZ(){
		float phi = 0, theta = 0;
		float phiFactor = PApplet.PI / (phiSteps - 1);
		float thetaFactor = PApplet.TWO_PI / (thetaSteps - 1);
		
		for(int p = 0; p < phiSteps; p++) {
			theta = 0.0f;
			for(int t = 0; t < thetaSteps; t++) {
//				coord[t][p] 
//				         = new PVector( radX * PApplet.sin(phi) * PApplet.cos(theta), 	// x
//				        		 		radY * PApplet.sin(phi) * PApplet.sin(theta),		// y
//				        		 		-radZ * PApplet.cos(phi)
//				        		 		);							// z			
				coord[t][p] 
				         = new PVector( radX * PApplet.sin(phi) * PApplet.cos(theta), 	// x
				        		 -radY * PApplet.cos(phi),
				        		 radZ * PApplet.sin(phi) * PApplet.sin(theta)				        		 		
				        		 		);							// z			

				theta += thetaFactor;
			}
			phi += phiFactor;
		}		
	}
	
	public void draw(){
		app.pushMatrix();
		app.translate(pos.x, -pos.y, pos.z);
		app.rotateX(rot.x);
		app.rotateY(rot.y);
		app.rotateZ(rot.z);
		
		PVector c, n;
		UV uv;

		for(int p = 0; p < phiSteps-1; p++) {
			app.beginShape(PApplet.QUAD_STRIP);
			
			if(skin != null)
				app.texture(skin);
			if(hasColor)
				app.fill(color);

			for(int t = 0; t < thetaSteps; t++) {
				c = coord[t][p];
				if(hasNormals){
					n = norm[t][p];
					app.normal(n.x, n.y, n.z);
				}
				if(skin != null){
					uv = uvLoc[t][p];	
					app.vertex(c.x, c.y, c.z, uv.u, uv.v);
				}
				else {
					app.vertex(c.x, c.y, c.z);					
				}

				
				c = coord[t][p+1];
				if(hasNormals){
					n = norm[t][p];
					app.normal(n.x, n.y, n.z);
				}
				if(skin != null){
					uv = uvLoc[t][p+1];	
					app.vertex(c.x, c.y, c.z, uv.u, uv.v);
				}
				else {
					app.vertex(c.x, c.y, c.z);					
				}
			}
			app.endShape(PApplet.CLOSE);
		}
		app.popMatrix();
	}

}
